
import java.net.*;
import java.io.*;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import nanoxml.XMLElement;

/**
 *
 * @author marduke
 * * basado en: http://zerioh.tripod.com/ressources/sockets.html
 */
public class ServidorNodo extends Thread {

    ServerSocket providerSocket;
    Socket connection = null;
    ObjectOutputStream out;
    ObjectInputStream in;
    String message;
    Vector vecinos = new Vector();
    Vector archivos = new Vector();
    String directorio = new String();
    String archivoTraza;

    public ServidorNodo(Vector vecinos, Vector archivos, String directorio, String archivoTraza) {
        this.vecinos = vecinos;
        this.archivoTraza = archivoTraza;
        this.archivos = archivos;
        this.directorio = directorio;
    }

    public void run() {



        while (true) {
            this.corrida();
        }

    }

    void imprimirTraza(String message) {


        try {
            // Create file

            FileWriter fstream = new FileWriter(archivoTraza, true);
            BufferedWriter out = new BufferedWriter(fstream);
            out.write(message + "\n");

            out.close();
        } catch (Exception e) {//Catch exception if any
            System.err.println("Error: Error el imprimir en el archivo traza\n");
        }

    }

    void corrida() {
        try {
            String retorno = new String();
            //1. creating a server socket
            providerSocket = new ServerSocket(1800, 10);
            
            //2. Wait for connection

            connection = providerSocket.accept();

            imprimirTraza("Se recibio una conexion desde: " + connection.getInetAddress().getHostAddress());

         
            //3. get Input and Output streams
            out = new ObjectOutputStream(connection.getOutputStream());
            out.flush();
            in = new ObjectInputStream(connection.getInputStream());
            sendMessage("Conexión exitosa");
            //4. The two parts communicate via the input and output streams
            do {
                try {
                    message = (String) in.readObject();

                    imprimirTraza("Se recibio este msj del cliente: " + message);

                    Scanner sc = new Scanner(message);

                    if (sc.hasNext()) {
                        String tmp = sc.next();
                        if (tmp.compareTo("Alcanzable") == 0) {

                            imprimirTraza("Es alcanzable");

                            String xml = sc.nextLine();
                            XMLElement visitados = new XMLElement();
                            visitados.parseString(xml);


                            try {
                                message = alcanzable(vecinos, visitados);
                            } catch (Exception ex) {
                                Logger.getLogger(ServidorNodo.class.getName()).log(Level.SEVERE, null, ex);
                            }
                            imprimirTraza("Le mande el siguiente mensaje al servidor: " + message);
                            sendMessage(message);

                        } else if (tmp.equals("Consulta")) {
                            String arg1 = sc.next();
                            String arg2 = sc.nextLine();

                            String prueba[] = arg2.split("%-XCTW-%");
                            try {

                                imprimirTraza("Es una consulta");



                            Scanner sc2 = new Scanner(prueba[0]);
                            String arg3 = "";
                            while (sc2.hasNext()){
                               arg3 = arg3 + " " +sc2.next();
                            }
                                message = consulta(directorio, arg1, arg3,
                                        prueba[1], prueba[2]);

                                sendMessage(message);
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        } else if (tmp.equals("Solicitud")) {

                            imprimirTraza("Es una solicitud");

                            String arg1 = sc.next();
                            String arg2 = sc.next();
                    
                            sendMessage("Recibe");
                            sendMessage(arg2);
                            enviarSolicitud(connection, directorio, arg1);
                        } else if (tmp.equals("bye")) {
                            sendMessage("bye");
                        } else {
                            
                            message = "Error";
                            sendMessage(message);
                        }
                    }
                } catch (ClassNotFoundException classnot) {
                    System.err.println("Data received in unknown format");
                }
            } while (!message.equals("bye"));
        } catch (SocketException socketE) {
            System.out.println("Hubo un error en el socket, este el servidor "
                    + "Nodo");
            System.exit(6);
        } catch (IOException ioException) {
            ioException.printStackTrace();
        } finally {
            //4: Closing connection
            try {
                in.close();
                out.close();
                providerSocket.close();
            } catch (IOException ioException) {
                ioException.printStackTrace();
            }
        }

    }

    String alcanzable(Vector vecinos, XMLElement visitados) throws Exception {

        //  System.out.println("<<<<<<<<<<<<<<<ESTOY EN LA FUNCION ALCANZABLE>>>>");

        String messageToServer;
        String xmlre = new String();
        String retorno = new String();
        InetAddress addr = InetAddress.getLocalHost();
        String hostAddress = addr.getHostAddress();


        XMLElement child = new XMLElement();
        child.setName("elemento");
        child.setAttribute("hostAddress", addr.getHostAddress());
        child.setAttribute("hostName", addr.getHostName());

        visitados.addChild(child);




        xmlre = visitados.toString();
        messageToServer = "Alcanzable " + xmlre;

        Iterator<String> it = vecinos.iterator();
        while (it.hasNext()) {
            String vecino = it.next();


            if (!fueVisitado(xmlre, vecino)) {
                imprimirTraza("Estoy verificando en el vecino: " + vecino);

                ClienteNodo clienteN = new ClienteNodo(messageToServer, vecino);
                String tmp = clienteN.run();
                if (!tmp.equals("Error")) {
                    xmlre = tmp;
                }
                imprimirTraza("El mensaje que recibi del vecino fue: " + xmlre);

            } else {

            }
            messageToServer = "Alcanzable " + xmlre;


        }
        retorno = xmlre;

        return retorno;
    }

    boolean fueVisitado(String xml, String vecino) {

        boolean retorno = false;

        try {

            XMLElement visitados = new XMLElement();
            visitados.parseString(xml);

            Enumeration childs = visitados.enumerateChildren();
            while (childs.hasMoreElements()) {
                XMLElement child = (XMLElement) childs.nextElement();
                String hostname = (String) child.getAttribute("hostName");
                String hostaddress = (String) child.getAttribute("hostAddress");

                

                if (hostaddress.equals(vecino) || hostname.equals(vecino)) {
                    return true;
                }
            }

        } catch (Exception uhE) {

        }

        return false;
    }

    void sendMessage(String msg) {
        try {
            out.writeObject(msg);
            out.flush();
            
        } catch (IOException ioException) {
            ioException.printStackTrace();
        }
    }

    private String consulta(String dirArchivos, String tipo, String cadena,
            String consulta, String visitado) throws Exception {

        String messageToServer;

        XMLElement retorno = new XMLElement();
        

        XMLElement conXml = new XMLElement();
        conXml.parseString(consulta);

        XMLElement maquina = new XMLElement();
        maquina.setName("maquina");
        InetAddress adr = InetAddress.getLocalHost();
        maquina.setAttribute("hostname", adr.getHostAddress());

        


        Iterator<String> ite = archivos.iterator();
        while (ite.hasNext()) {
            String aux = (String) ite.next();
            XMLElement children = consultaXML(dirArchivos, aux, tipo, cadena);
            maquina.addChild(children);
        }

        conXml.addChild(maquina);


        
        XMLElement visXml = new XMLElement();
        visXml.parseString(visitado);

        InetAddress addr = InetAddress.getLocalHost();



        XMLElement child = new XMLElement();
        child.setName("elemento");
        child.setAttribute("hostAddress", addr.getHostAddress());
        child.setAttribute("hostName", addr.getHostName());

        visXml.addChild(child);


        
        String xmlre = visXml.toString();
        String xmlco = conXml.toString();

        messageToServer = "Consulta " + tipo + " " + cadena + "%-XCTW-%" + xmlco
                + "%-XCTW-%" + xmlre;

        Iterator<String> it = vecinos.iterator();
        while (it.hasNext()) {
            String vecino = it.next();

            if (!fueVisitado(xmlre, vecino)) {
                ClienteNodo clienteN = new ClienteNodo(messageToServer, vecino);
                String tmp = clienteN.run();
                String tmp2[] = tmp.split("%-XCTW-%");
                xmlre = tmp2[1];
                xmlco = tmp2[0];
            } else {
                System.out.println("Este ya fue visitado.");
            }
            messageToServer = "Consulta " + tipo + " " + cadena + "%-XCTW-%"
                    + xmlco + "%-XCTW-%" + xmlre;
            

        }



        return xmlco + "%-XCTW-%" + xmlre;
    }

    private XMLElement consultaXML(String dirArchivo, String archivo,
            String tipo, String cadena) {
        XMLElement retorno = new XMLElement();

        try {

            String autor = "";
            String titulo = "";
            String descripcion = "";

            XMLElement xml = new XMLElement();
            if (!(dirArchivo.substring(dirArchivo.length() - 2)).equals("/")) {
                dirArchivo += "/";
            }

            FileReader reader = new FileReader(dirArchivo + archivo + ".xml");
            xml.parseFromReader(reader);

            Enumeration children = xml.enumerateChildren();
            if (tipo.compareTo("clave") == 0) {
                while (children.hasMoreElements()) {
                    XMLElement child = (XMLElement) children.nextElement();
                    if (child.getName().compareTo("palabrasClave") == 0) {
                        Enumeration children2 = child.enumerateChildren();
                        while (children2.hasMoreElements()) {
                            XMLElement child2 = (XMLElement) children2.nextElement();

                            if (((String) child2.getAttribute("palabra"))
                                    .indexOf(cadena) >= 0) {
                                Enumeration children3 = xml.enumerateChildren();
                                while (children3.hasMoreElements()) {
                                    XMLElement child3 = (XMLElement) children3.nextElement();
                                    if (child3.getName().compareTo("titulo") == 0) {
                                        titulo = child3.getContent();
                                    } else if (child3.getName().
                                            compareTo("descripcion") == 0) {
                                        descripcion = child3.getContent();
                                    } else if (child3.getName().
                                            compareTo("autor") == 0) {
                                        autor = (String) child3.getAttribute("name");
                                    }
                                }

                                retorno = enviarConsulta(archivo + ".jpg",
                                        titulo, autor, descripcion);
                            }
                        }
                    }
                }
            } else {                
                while (children.hasMoreElements()) {
                    XMLElement child = (XMLElement) children.nextElement();
                    if (child.getName().equals("titulo")) {
                        if (((String)child.getContent()).indexOf(cadena) >= 0) {
                            Enumeration children3 = xml.enumerateChildren();
                            while (children3.hasMoreElements()) {
                                XMLElement child3 = (XMLElement) children3.nextElement();
                                if (child3.getName().compareTo("titulo") == 0) {
                                    titulo = child3.getContent();
                                } else if (child3.getName().
                                        compareTo("descripcion") == 0) {
                                    descripcion = child3.getContent();
                                } else if (child3.getName().
                                        compareTo("autor") == 0) {
                                    autor = (String) child3.getAttribute("name");
                                }
                            }
                            retorno = enviarConsulta(archivo + ".jpg",
                                    titulo, autor, descripcion);
                        }
                    }
                }
            }
        } catch (Exception e) {
        }

        return retorno;
    }

    private XMLElement enviarConsulta(String nombreArchivo, String titulo,
            String autor, String descripcion) throws Exception {

        XMLElement child = new XMLElement();
        child.setName("foto");
        child.setAttribute("nombreArchivo", nombreArchivo);
        child.setAttribute("autor", autor);
        child.setAttribute("titulo", titulo);
        child.setAttribute("descripcion", descripcion);
        return child;
    }

    private void enviarSolicitud(Socket sock, String directorio, String archivo) {
        try {

//    ServerSocket servsock = new ServerSocket(13267);
            while (true) {
                System.out.println("Waiting...");

//      Socket sock = servsock.accept();
                System.out.println("Accepted connection : " + sock);

                // sendfile
                System.out.println(directorio + "/" + archivo);
                File myFile = new File(directorio + "/" + archivo);
                byte[] mybytearray = new byte[(int) myFile.length()];
                FileInputStream fis = new FileInputStream(myFile);
                BufferedInputStream bis = new BufferedInputStream(fis);
                bis.read(mybytearray, 0, mybytearray.length);
                OutputStream os = sock.getOutputStream();
                System.out.println("Sending...");
                os.write(mybytearray, 0, mybytearray.length);
                os.flush();
                sock.close();
            }
        } catch (Exception e) {
        }
    }
}
